#include "stdafx.h"
#include <memory>
#include <string>
#include <iostream>

#include "PImpl.h"
#include "NamedMockData.h"

extern long __auto_id_;
// PImpl::Impl should  be only used in this cpp file
// And we assume it's a value calss
class PImpl::Impl {// defined privately here
  // ... all private data and functions: all of these
  // can now change without recompiling callers ...
public:
	// One type of constructor
	Impl() : _data(new NamedMockData<int, 100000L>), _id(__auto_id_++) {
		std::cout << "Calling PImpl::Impl constructor" << std::endl;
	}

	virtual ~Impl(void) {
		std::cout << "Calling PImpl::Impl destructor" << std::endl;
	}

	Impl(const Impl& right) : _data(new NamedMockData<int, 100000L>(*right._data)), 
		_id(__auto_id_++) {
		std::cout << "Calling PImpl::Impl copy constructor" << std::endl; 
	}
	
	Impl& operator=(const Impl& right) {
		std::cout << "Calling PImpl::Impl assign operator copy constructor" << std::endl; 

		if (this == &right) return *this;

		this->_data.reset(new NamedMockData<int, 100000L>(*right._data));
		this->_id = __auto_id_++;
	}

	Impl(Impl&& right) : _data(std::move(right._data)), _id(right._id) {
		std::cout << "Calling PImpl::Impl move copy constructor" << std::endl;
	}

	Impl& operator=(Impl&& right) {
		std::cout << "Calling PImpl::Impl move assign operator constructor" << std::endl; 

		// the unique_ptr has reset the original point firstly
		this->_data = std::move(right._data);
		this->_id = right._id;
		return *this;
	}

	void print() {
		std::cout << "Calling PImpl::Impl::Print: id => " << _id << std::endl; 
	}

private:
	std::unique_ptr<NamedMockData<int, 100000L>> _data;
	long _id;
};

PImpl::PImpl(void) : _impl(new Impl())
{
	std::cout << "Calling PImpl constructor" << std::endl;
}

void PImpl::print(void)
{
	std::cout << "Calling PImpl::Print" << std::endl; 
	this->_impl->print();
}

void PImpl::swap(PImpl& right)
{
	std::swap(this->_impl, right._impl);
}

PImpl::~PImpl(void)
{
	std::cout << "Calling PImpl destructor" << std::endl;
}

void PImplAction(void)
{
	std::cout << __FUNCTION__ << std::endl;
	std::unique_ptr<PImpl> printer(new PImpl);
	printer->print();
}

void PImpSwapAction(void)
{
	std::cout << __FUNCTION__ << std::endl;

	std::unique_ptr<PImpl> p1(new PImpl);
	std::unique_ptr<PImpl> p2(new PImpl);

	std::cout << "\n:::Before Swap" << std::endl;
	p1->print();
	p2->print();

	p1.swap(p2);
	std::cout << "\n:::After Swap" << std::endl;

	p1->print();
	p2->print();

	std::cout << "\n:::\n" << std::endl;
}

// References:
// http://msdn.microsoft.com/en-us/library/windows/apps/hh438477.aspx
// http://msdn.microsoft.com/en-us/library/windows/apps/hh438479.aspx